﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Clandestine;

namespace Slick.MapEditor.Tools
{
    public class TilePainter : ITool
    {
        public Map Map;
        public string Name { get { return "Tile Painter"; } }

        /// <summary>
        /// The sprite used for the previews as we drag the mouse.
        /// </summary>
        private static Sprite hoverSprite;

        /// <summary>
        /// Whether we're erasing or not. (i.e. whether right mouse button was clicked or not)
        /// </summary>
        private bool IsErase = false;

        /// <summary>
        /// Whether the mouse button is being held down.
        /// </summary>
        private bool down = false;

        /// <summary>
        /// The point at which the mouse was pressed down - to allow dragging.
        /// </summary>
        private Point downPosition = new Point();

        /// <summary>
        /// Current position as we drag the mouse.
        /// </summary>
        private Point currentPosition = new Point();

        public TilePainter(Map map)
        {
            this.Map = map;
            hoverSprite = new Sprite(Texture.GetTexture("white.png"));
            hoverSprite.Visible = false;
            hoverSprite.Color = new GLColor(0f, 1f, 1f, 0.6f);
        }

        ~TilePainter()
        {
            hoverSprite.Dispose();
        }

        public void Focus()
        {
        }

        public void Unfocus()
        {
        }

        public void MouseUpLeft(int mouseX, int mouseY, int tileX, int tileY)
        {
            // No longer down.
            down = false;

            // Check they're valid!
            if (downPosition.Y == -1 || downPosition.Y == -1)
                return;

            // Paint the tiles, false to show not temp!
            doPaintTileOp(tileX, tileY, false);

            downPosition = currentPosition = new Point(-1, -1);
        }

        public void MouseDownLeft(int mouseX, int mouseY, int tileX, int tileY)
        {
            // If we're not already erasing
            if (!(IsErase && down))
            {
                // remember where we went down and that we went down
                downPosition = currentPosition = new Point(tileX, tileY);
                down = true;
            }
            else
            {
                // if we were erasing, just stop erasing.
                down = IsErase = false;
                downPosition = currentPosition = new Point(-1, -1);
                Map.Layers.ResetTempLayer();
                hoverSprite.Visible = false;
            }
        }

        public void MouseUpRight(int mouseX, int mouseY, int tileX, int tileY)
        {
            // Are we erasing?
            if (IsErase && down)
            {
                // get co-ords of top left of the selection - note, we can move the mouse behind the original point
                // so must handle this!
                Point topLeft = new Point();
                topLeft.X = (currentPosition.X < downPosition.X ? currentPosition.X : downPosition.X);
                topLeft.Y = (currentPosition.Y < downPosition.Y ? currentPosition.Y : downPosition.Y);

                // get co-ords of the bottom right
                Point bottomRight = new Point();
                bottomRight.X = (currentPosition.X > downPosition.X ? currentPosition.X : downPosition.X);
                bottomRight.Y = (currentPosition.Y > downPosition.Y ? currentPosition.Y : downPosition.Y);

                // Perform the erase
                for (int y = topLeft.Y; y <= bottomRight.Y; y++)
                    for (int x = topLeft.X; x <= bottomRight.X; x++)
                        Map.EraseTile(x, y);

                // Stop erasing/hover mode
                IsErase = down = hoverSprite.Visible = false;
                downPosition = currentPosition = new Point(-1, -1);
            }
        }


        public void MouseDownRight(int mouseX, int mouseY, int tileX, int tileY)
        {
            // Right-click.
            if (!down)
            {
                IsErase = down = true;
                currentPosition = downPosition = new Point(tileX, tileY);
                hoverSprite.Visible = true;
                hoverSprite.Layer = (float)Map.Layers.SelectedLayer.Depth + 0.001f;
                hoverSprite.Position = new Point(downPosition.X * Program.GridSize,
                    downPosition.Y * Program.GridSize);
                hoverSprite.ScaleX = (tileX + 1 - downPosition.X) * Program.GridSize;
                hoverSprite.ScaleY = (tileY + 1 - downPosition.Y) * Program.GridSize;
            }
            else
            {
                down = false;
                downPosition = currentPosition = new Point(-1, -1);
            }

            Map.Layers.ResetTempLayer();
        }

        public void MouseMove(int mouseX, int mouseY, int tileX, int tileY)
        {
            // is the mouse held-down?
            if (down && !IsErase)
            {
                // if we're not erasing, we must be painting!
                // possibly update preview, and remember mouse position.

                if ((currentPosition.X != tileX) && (currentPosition.Y != tileY)) // i.e. have we moved the mouse?
                    doPaintTileOp(tileX, tileY, true); // update temp preview! :)

                currentPosition = new Point(tileX, tileY);
            }
            else if (down && IsErase)
            {
                // Must be erasing,
                currentPosition = new Point(tileX, tileY);

                // get co-ords of top left of the selection - note, we can move the mouse behind the original point
                // so must handle this!
                Point topLeft = new Point();
                topLeft.X = (currentPosition.X < downPosition.X ? currentPosition.X : downPosition.X);
                topLeft.Y = (currentPosition.Y < downPosition.Y ? currentPosition.Y : downPosition.Y);

                // get co-ords of the bottom right
                Point bottomRight = new Point();
                bottomRight.X = (currentPosition.X > downPosition.X ? currentPosition.X : downPosition.X);
                bottomRight.Y = (currentPosition.Y > downPosition.Y ? currentPosition.Y : downPosition.Y);

                hoverSprite.Visible = true;
                hoverSprite.Layer = (float)Map.Layers.SelectedLayer.Depth + 0.001f;
                hoverSprite.Position = new Point(topLeft.X * Program.GridSize,
                    topLeft.Y * Program.GridSize);
                hoverSprite.ScaleX = (bottomRight.X + 1 - topLeft.X) * Program.GridSize;
                hoverSprite.ScaleY = (bottomRight.Y + 1 - topLeft.Y) * Program.GridSize;
            }
            else
            {
                if (Tilesets.SelectedTiles.Count > 0)
                {
                    downPosition = new Point(tileX, tileY);
                    doPaintTileOp(tileX, tileY, true);
                    downPosition = new Point(-1, -1);
                }
                else
                    Map.Layers.ResetTempLayer();
            }
        }

        private void doPaintTileOp(int tileX, int tileY, bool tmp)
        {
            DateTime dtStart = DateTime.Now;

            Map.Layers.ResetTempLayer();

            List<List<string>> tst = Tilesets.SelectedTiles;

            int rdx, rdy, rux, ruy;

            rdx = (downPosition.X <= tileX ? downPosition.X : tileX);
            rdy = (downPosition.Y <= tileY ? downPosition.Y : tileY);
            rux = (downPosition.X <= tileX ? tileX : downPosition.X);
            ruy = (downPosition.Y <= tileY ? tileY : downPosition.Y);

            rux++; // just
            ruy++; // because (round to next tile basically)

            DateTime dtPaintStart = DateTime.Now;

            lock (tst)
            {
                if (tst.Count == 0)
                    return;

                for (int y = 0; y < (ruy - rdy); y += tst.Count)
                    for (int x = 0; x < (rux - rdx); x += tst[0].Count)
                        Map.PaintTile(rdx + x, rdy + y, tmp);
            }

            Log.v("PaintTileOp took " + (DateTime.Now - dtStart).TotalMilliseconds.ToString()
                + " ms.");
            Log.v("(" + (DateTime.Now - dtPaintStart).TotalMilliseconds.ToString()
                + "ms of that was spent painttile-ing.)");
        }

    }
}
